package com.helloworld.common.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.cfw.exception.BizException;
import com.cfw.properties.CustomProperties;

@Controller
@RequestMapping("/file/*")
public class FileController {
	private static final Logger logger = LoggerFactory.getLogger(FileController.class); 

	@RequestMapping(value = "/upload", produces = "text/plain;charset=UTF-8", method=RequestMethod.POST)
	@ResponseBody
	public String upload(HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile file) {
		logger.debug("*** " + CustomProperties.getProperty("upload.temp.location"));
		
		String oriFileName = file.getOriginalFilename();
		String ext = FilenameUtils.getExtension(oriFileName);
		String fileName = RandomStringUtils.randomAlphanumeric(30);
		if (StringUtils.isNoneEmpty(ext)) { fileName += ('.' + ext); }
		String fullFileName = CustomProperties.getProperty("upload.temp.location") + File.separator + fileName;
		
		File newFile = new File(fullFileName);
		
		try {
			file.transferTo(newFile);
		} catch (IllegalStateException e) {
			throw new BizException();
		} catch (IOException e) {
			throw new BizException();
		}
		
		// i.e. Save the file to a temporary location or database
		logger.debug("** name:" + oriFileName);
		logger.debug("** newName:" + fileName);
		logger.debug("** size:" + file.getSize());
		logger.debug("** ext:" + ext);
		
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("name", oriFileName);
		jsonObject.put("newName", fileName);
		jsonObject.put("size", file.getSize());
		jsonObject.put("ext", ext);

		JSONArray jsonArray = new JSONArray();
		jsonArray.add(jsonObject);
		
		return jsonArray.toJSONString();
	}

	/**
	 * deprecated: IE9 이하, jquery-file-upload를 사용할 경우 json 데이터를 파일로 내려받게 됨 
	 * @param request
	 * @param response
	 * @param file
	 * @return
	 */
	@Deprecated
	@RequestMapping(value = "/file.json", method=RequestMethod.POST)
	@ResponseBody
	public List<Map<String, Object>> uploadJson(HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile file) {
		logger.debug("*** " + CustomProperties.getProperty("upload.temp.location"));
		
		String oriFileName = file.getOriginalFilename();
		String ext = FilenameUtils.getExtension(oriFileName);
		String fileName = CustomProperties.getProperty("upload.temp.location") + "/" + RandomStringUtils.randomAlphanumeric(8);
		if (StringUtils.isNoneEmpty(ext)) { fileName += ('.' + ext); }
		
		File newFile = new File(fileName);
		
		try {
			file.transferTo(newFile);
		} catch (IllegalStateException e) {
			throw new BizException();
		} catch (IOException e) {
			throw new BizException();
		}
		
		// i.e. Save the file to a temporary location or database
		logger.debug("** oriFileName:" + oriFileName);
		logger.debug("** fileName:" + fileName);
		logger.debug("** ext:" + ext);
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("name", file.getOriginalFilename());
		map.put("newName", fileName);
		map.put("size", file.getSize());
		map.put("ext", ext);

		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		list.add(map);
		
		return list;
	}
	
	private enum Status {OK, FILE_NOT_FOUND, EXCEPTION};
	
	@RequestMapping(value = "/download", method = {RequestMethod.GET, RequestMethod.POST})
	public void download(final HttpServletRequest request, final HttpServletResponse response, @RequestParam Map<String, Object> params) throws IOException {	
		Status state = Status.OK;

		FileInputStream fileInputStream = null;
		BufferedInputStream bufferedInputStream = null;
		ServletOutputStream servletOutputStream = null;
		BufferedOutputStream bufferedOutputStream = null;
		
		try {
			final String paraFile =	request.getParameter("file");
			final String fileName = CustomProperties.getProperty("attach.base.location") + File.separator + paraFile;
			final File file = new File(fileName);

			fileInputStream = new FileInputStream(file);
			bufferedInputStream = new BufferedInputStream(fileInputStream);
			
			response.reset();
			/* 전송방식이 'file'일 경우, browser 별 설정도 추가하여야 한다 */
		    response.setHeader("Content-type", "application/octet-stream");
		    response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
		    response.setHeader("Content-Length", Long.toString(file.length()) );

		    response.setHeader("Content-Transfer-Encoding", "binary");
		    response.setHeader("Pragma", "no-cache");
		    response.setHeader("Cache-Control", "private");
		    response.setHeader("Expires", "0");

			int nReadSize = 0;
			final int bufferCapacity = bufferedInputStream.available() + 1;
			
			byte[] buf = null;
			buf = new byte[bufferCapacity];
			
			servletOutputStream = response.getOutputStream();
			bufferedOutputStream = new BufferedOutputStream(servletOutputStream);

			nReadSize = bufferedInputStream.read(buf);
			while (nReadSize != -1) {
				bufferedOutputStream.write(buf, 0, nReadSize);
				nReadSize = bufferedInputStream.read(buf);				
			}
			
			bufferedOutputStream.flush();
			servletOutputStream.flush();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			
			state = Status.FILE_NOT_FOUND;
		} catch (Exception e) {
			e.printStackTrace();
			
			state = Status.EXCEPTION;
		} finally {
			if (fileInputStream != null) { try { fileInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
			if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
			if (bufferedOutputStream != null) { try { bufferedOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
			if (servletOutputStream != null) { try { servletOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
		}
		if (state != Status.OK) {
			response.reset();
		    response.setHeader("Content-type", "text/html;charset=UTF-8");
		    response.setHeader("Content-Transfer-Encoding", "chunked");

			final StringBuffer buf = new StringBuffer(100);
			buf.append("<script type='text/javascript'>alert('화일다운로드 중 오류가 발생하였습니다.')</script>");

			final PrintWriter writer = response.getWriter();
			writer.print(buf.toString());
		}				
	}	
	
		public void writeResponseFileStream(String fileName, HttpServletResponse response) {
			boolean success = true;
			FileInputStream fileInputStream = null;
			BufferedInputStream bufferedInputStream = null;
			ServletOutputStream servletOutputStream = null;
			BufferedOutputStream bufferedOutputStream = null;
			
			Cookie cookie = new Cookie("fileDownload", "true");
			cookie.setSecure(false);
			cookie.setMaxAge(-1);
			cookie.setPath("/");
			try {
				final File file = new File(fileName);

				fileInputStream = new FileInputStream(file);
				bufferedInputStream = new BufferedInputStream(fileInputStream);
				
				response.reset();
				/* 전송방식이 'file'일 경우, browser 별 설정도 추가하여야 한다 */
			    response.setHeader("Content-type", "application/octet-stream");
			    response.setHeader("Content-Disposition", "attachment; filename=" + FilenameUtils.getName(fileName));
			    response.setHeader("Content-Length", Long.toString(file.length()) );

			    response.setHeader("Content-Transfer-Encoding", "binary");
			    response.setHeader("Pragma", "no-cache");
			    response.setHeader("Cache-Control", "private");
			    response.setHeader("Expires", "0");

				response.addCookie(cookie);
			    
				byte[] buf = new byte[bufferedInputStream.available() + 1];
				
				servletOutputStream = response.getOutputStream();
				bufferedOutputStream = new BufferedOutputStream(servletOutputStream);
				
				int nReadSize = bufferedInputStream.read(buf);
				while (nReadSize != -1) {
					bufferedOutputStream.write(buf, 0, nReadSize);
					nReadSize = bufferedInputStream.read(buf);				
				}
				
				bufferedOutputStream.flush();
				servletOutputStream.flush();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				success = false;
			} catch (Exception e) {
				e.printStackTrace();
				success = false;
			} finally {
				if (fileInputStream != null) { try { fileInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (bufferedOutputStream != null) { try { bufferedOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (servletOutputStream != null) { try { servletOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
			}			
			
			if (!success) {
				throw new BizException("E9000");
			}
		}
		
		public void writeResponseResourceStream(String fileName, HttpServletRequest request, HttpServletResponse response) {
			boolean success = true;
			FileInputStream fileInputStream = null;
			BufferedInputStream bufferedInputStream = null;
			ServletOutputStream servletOutputStream = null;
			BufferedOutputStream bufferedOutputStream = null;
			
			try {
				final File file = new File(fileName);

				fileInputStream = new FileInputStream(file);
				bufferedInputStream = new BufferedInputStream(fileInputStream);
				
				ServletContext ctx = request.getSession().getServletContext();
				response.reset();
				response.setContentType(ctx.getMimeType(fileName));
				
				logger.debug("*** MimeType:" + ctx.getMimeType(fileName));
				
				response.setContentLength((int)file.length());
			    
				byte[] buf = new byte[bufferedInputStream.available() + 1];
				
				servletOutputStream = response.getOutputStream();
				bufferedOutputStream = new BufferedOutputStream(servletOutputStream);
				
				int nReadSize = bufferedInputStream.read(buf);
				while (nReadSize != -1) {
					bufferedOutputStream.write(buf, 0, nReadSize);
					nReadSize = bufferedInputStream.read(buf);				
				}
				
				bufferedOutputStream.flush();
				servletOutputStream.flush();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				success = false;
			} catch (Exception e) {
				e.printStackTrace();
				success = false;
			} finally {
				if (fileInputStream != null) { try { fileInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (bufferedOutputStream != null) { try { bufferedOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
				if (servletOutputStream != null) { try { servletOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
			}			
			
			if (!success) {
				throw new BizException("E9000");
			}
		}		
		
	@RequestMapping(value = "/download2", method = {RequestMethod.GET, RequestMethod.POST})
	public void download2(final HttpServletRequest request, final HttpServletResponse response, @RequestParam Map<String, Object> params) {
		if (true) { throw new BizException("nosession"); }
		
		String fileName = "c:/temp/dir.txt";
		
		writeResponseFileStream(fileName, response);
	}
	
	@RequestMapping(value = "/download3", method = {RequestMethod.GET, RequestMethod.POST})
	public void download3(final HttpServletRequest request, final HttpServletResponse response, @RequestParam Map<String, Object> params) {
		String fileName = "c:/temp/helloworld111.zip";
		
		writeResponseFileStream(fileName, response);
	}

	@RequestMapping(value = "/download4", method = {RequestMethod.GET, RequestMethod.POST})
	public void download4(final HttpServletRequest request, final HttpServletResponse response, @RequestParam Map<String, Object> params) {
		String fileName = "c:/temp/4.jpg";
		
		writeResponseResourceStream(fileName, request, response);
	}
	
}
